package com.mxx.tomcat;

import com.mxx.common.utils.ClassUtil;
import com.mxx.tomcat.util.IpUtil;
import com.mxx.common.conv.JsonUtil;
import com.mxx.tomcat.aspect.AspectEntry;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.io.InputStreamSource;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

/**
 * @Auther: Mr. Zeng
 * @Date: 2020/5/7 11:32
 * @Description: 暂无描述
 */
@Aspect
@Component
@Slf4j
@EnableAspectJAutoProxy
public class RequestMappingAspect {

    private Map<Method, Boolean> map;
    private AspectEntry aspectEntry;


    @Autowired
    public RequestMappingAspect(ApplicationContext applicationContext) {

        try {
            map = new HashMap<>();
            this.aspectEntry = applicationContext.getBean(AspectEntry.class);
        } catch (Exception ex) {
            this.aspectEntry = new AspectEntry();
        }


    }

    @Pointcut(" @annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void requestMapping() {
    }

    @Pointcut(" @annotation(org.springframework.web.bind.annotation.PostMapping)")
    public void postMapping() {
    }

    @Pointcut(" @annotation(org.springframework.web.bind.annotation.GetMapping)")
    public void getMapping() {
    }

    @Before("requestMapping()||postMapping()||getMapping()")
    public void doBeforeAdvice(JoinPoint joinPoint) {


        Object[] dataArray = joinPoint.getArgs();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        if (!isPrint(signature.getMethod())) {
            return;
        }
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();

        StringBuffer sb = new StringBuffer();
        sb.append("\n");
        HttpServletRequest request = requestAttributes.getRequest();

        Method method = signature.getMethod();
        sb.append("|  URL  :").append(request.getRequestURL().toString())
                .append("\t IP :").append(IpUtil.getRealIP(request))
                .append("\t Method :").append(request.getMethod())
                .append("\t Content-Type :").append(request.getContentType())
                .append("\t Object :")
                .append(joinPoint.getTarget().getClass().getSimpleName()).append(".").append(method.getName()).append("\n");
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            sb.append("|  Name :").append(parameter.getName()).append("\t\t")
                    .append("Type :").append(parameter.getParameterizedType().getTypeName()).append("\t\tData :");
            Object data = dataArray[i];
            if (data instanceof Serializable) {
                if (data instanceof String) {
                    sb.append(data);
                } else {
                    sb.append(JsonUtil.toJSONString(data));
                }
            }
            if (data instanceof InputStreamSource) {
                sb.append("fileData :").append(data);
            } else {
                sb.append(data == null ? "null" : data);
            }
            sb.append("\n");
        }


        log.info(sb.toString());
    }

    private boolean isPrint(Method method) {
        Boolean flag = map.get(method);
        if (flag == null) {
            synchronized (method) {
                if (aspectEntry.isBlockAll()) {
                    Annotation[] annotations = method.getAnnotations();
                    for (Annotation annotation : annotations) {
                        if (aspectEntry.getAllowAnnotation().contains(annotation.annotationType())) {
                            map.put(method, true);
                            return true;
                        }
                    }
                    map.put(method, false);
                    return false;
                } else {
                    Annotation[] annotations = method.getAnnotations();
                    for (Annotation annotation : annotations) {
                        if (aspectEntry.getAnnotationClassList().contains(annotation.annotationType())) {
                            map.put(method, false);
                            return false;
                        }
                    }
                    Class domainClass = method.getDeclaringClass();
                    for (Class superInterface : aspectEntry.getSuperInterfaceList()) {
                        if (ClassUtil.isSuperInterface(domainClass, superInterface)) {
                            map.put(method, false);
                            return false;
                        }
                    }
                    Class superClass = domainClass.getSuperclass();
                    if (superClass == null) {
                        map.put(method, false);
                        return false;
                    }
                    for (Class checkSuperClass : aspectEntry.getSuperClassList()) {
                        if (superClass.equals(checkSuperClass)) {
                            map.put(method, false);
                            return false;
                        }

                    }
                    return true;
                }

            }


        } else {
            return flag;
        }
    }

    public static void main(String[] args) throws NoSuchMethodException {
        AspectEntry aspectEntry = new AspectEntry().addAnnotationClass(Before.class);
        Method method = RequestMappingAspect.class.getMethod("doBeforeAdvice", JoinPoint.class);

        System.out.println();

    }

}
